#include "stdafx.h"

#include "hwinsynch.h"

namespace harlinn
{
    namespace windows
    {
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS(AbandonedMutexException)

        // ----------------------------------------------------------------------
        // WaitableHandle
        // ----------------------------------------------------------------------
        HWIN_EXPORT void WaitableHandle::ThrowAbandonedMutexException()
        {
            throw AbandonedMutexException();
        }


        HWIN_EXPORT bool WaitableHandle::Wait()
        {
            return Wait(Timeout::Infinite);
        }
        HWIN_EXPORT bool WaitableHandle::Wait(DWORD timeoutInMillis)
        {
            if(!IsValid())
            {
                ThrowInvalidHandle();
            }

            auto ret = WaitForSingleObject(Value(), timeoutInMillis);
            if(ret == WAIT_ABANDONED)
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            return ret != WAIT_TIMEOUT;
        }
        HWIN_EXPORT bool WaitableHandle::Wait(const TimeSpan& timeout)
        {
            DWORD timeoutInMillis = DWORD(timeout.TotalMilliseconds());
            return Wait(timeoutInMillis);
        }

        HWIN_EXPORT bool WaitableHandle::Wait(bool alertable)
        {
            return Wait(Timeout::Infinite,alertable);
        }
        HWIN_EXPORT bool WaitableHandle::Wait(DWORD timeoutInMillis, bool alertable)
        {
            if(!IsValid())
            {
                ThrowInvalidHandle();
            }

            auto ret = WaitForSingleObjectEx(Value(), timeoutInMillis,alertable?TRUE:FALSE);
            if(ret == WAIT_ABANDONED)
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            return ret != WAIT_TIMEOUT;
        }
        HWIN_EXPORT bool WaitableHandle::Wait(const TimeSpan& timeout, bool alertable)
        {
            DWORD timeoutInMillis = DWORD(timeout.TotalMilliseconds());
            return Wait(timeoutInMillis,alertable);
        }

        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis)
        {
            if(numberOfHandles > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }

            auto ret = WaitForMultipleObjects(DWORD(numberOfHandles),handles,FALSE,timeoutInMillis);
            if((ret >= WAIT_ABANDONED_0)&&(ret < (WAIT_ABANDONED_0 + numberOfHandles) ))
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            if(ret == WAIT_TIMEOUT)
            {
                return WaitTimeout;
            }
            return ret - WAIT_OBJECT_0;
        }

        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis,bool alertable)
        {
            if(numberOfHandles > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }

            auto ret = WaitForMultipleObjectsEx(DWORD(numberOfHandles),handles,FALSE,timeoutInMillis,alertable?TRUE:FALSE);
            if((ret >= WAIT_ABANDONED_0)&&(ret < (WAIT_ABANDONED_0 + numberOfHandles) ))
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            if(ret == WAIT_TIMEOUT)
            {
                return WaitTimeout;
            }
            return ret - WAIT_OBJECT_0;
        }


        HWIN_EXPORT int WaitableHandle::WaitForAllOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis)
        {
            if(numberOfHandles > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }

            auto ret = WaitForMultipleObjects(DWORD(numberOfHandles),handles,TRUE,timeoutInMillis);
            if((ret >= WAIT_ABANDONED_0)&&(ret < (WAIT_ABANDONED_0 + numberOfHandles) ))
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            if(ret == WAIT_TIMEOUT)
            {
                return WaitTimeout;
            }
            return ret - WAIT_OBJECT_0;
        }

        HWIN_EXPORT int WaitableHandle::WaitForAllOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis,bool alertable)
        {
            if(numberOfHandles > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }

            auto ret = WaitForMultipleObjectsEx(DWORD(numberOfHandles),handles,TRUE,timeoutInMillis,alertable?TRUE:FALSE);
            if((ret >= WAIT_ABANDONED_0)&&(ret < (WAIT_ABANDONED_0 + numberOfHandles) ))
            {
                ThrowAbandonedMutexException();
            }
            if(ret == WAIT_FAILED)
            {
                ThrowLastOSError();
            }
            if(ret == WAIT_TIMEOUT)
            {
                return WaitTimeout;
            }
            return ret - WAIT_OBJECT_0;
        }

        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis)
        {
            size_t size = waitableHandles.size();
            if( size > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }
            HANDLE handles[MAXIMUM_WAIT_OBJECTS];
            for(size_t i = 0; i < size; i++)
            {
                std::shared_ptr< WaitableHandle > waitableHandle = waitableHandles.at(i);
                handles[i] = waitableHandle->GetHANDLE();
            }
            return WaitForAnyOf(size,handles,timeoutInMillis);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis,bool alertable)
        {
            size_t size = waitableHandles.size();
            if( size > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }
            HANDLE handles[MAXIMUM_WAIT_OBJECTS];
            for(size_t i = 0; i < size; i++)
            {
                std::shared_ptr< WaitableHandle > waitableHandle = waitableHandles.at(i);
                handles[i] = waitableHandle->GetHANDLE();
            }
            return WaitForAnyOf(size,handles,timeoutInMillis,alertable);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis)
        {
            size_t size = waitableHandles.size();
            if( size > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }
            HANDLE handles[MAXIMUM_WAIT_OBJECTS];
            for(size_t i = 0; i < size; i++)
            {
                std::shared_ptr< WaitableHandle > waitableHandle = waitableHandles.at(i);
                handles[i] = waitableHandle->GetHANDLE();
            }
            return WaitForAllOf(size,handles,timeoutInMillis);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis,bool alertable)
        {
            size_t size = waitableHandles.size();
            if( size > MAXIMUM_WAIT_OBJECTS)
            {
                throw ArgumentException("numberOfHandles");
            }
            HANDLE handles[MAXIMUM_WAIT_OBJECTS];
            for(size_t i = 0; i < size; i++)
            {
                std::shared_ptr< WaitableHandle > waitableHandle = waitableHandles.at(i);
                handles[i] = waitableHandle->GetHANDLE();
            }
            return WaitForAllOf(size,handles,timeoutInMillis,alertable);
        }


        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles)
        {
            return WaitForAnyOf(waitableHandles,Timeout::Infinite);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,bool alertable)
        {
            return WaitForAnyOf(waitableHandles,Timeout::Infinite,alertable);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles)
        {
            return WaitForAllOf(waitableHandles,Timeout::Infinite);
        }
        HWIN_EXPORT int WaitableHandle::WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,bool alertable)
        {
            return WaitForAllOf(waitableHandles,Timeout::Infinite,alertable);
        }


        // ----------------------------------------------------------------------
        // EventWaitHandle
        // ----------------------------------------------------------------------

        HWIN_EXPORT HANDLE EventWaitHandle::CreateEvent(SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess)
        {
            DWORD flags = 0;
            if(initiallySet)
            {
                flags = CREATE_EVENT_INITIAL_SET;
            }
            if(manualReset)
            {
                flags |= CREATE_EVENT_MANUAL_RESET;
            }

            auto result = CreateEventExW(eventAttributes, name, flags, DWORD(desiredAccess));

            if(result == 0)
            {
                ThrowLastOSError();
            }
            return result;
        }

        HWIN_EXPORT HANDLE EventWaitHandle::Open(wchar_t* name,EventWaitHandleRights desiredAccess, bool inheritHandle)
        {
            auto result = OpenEventW(DWORD(desiredAccess),inheritHandle,name);
            if(result == 0)
            {
                ThrowLastOSError();
            }
            return result;
        }


        HWIN_EXPORT EventWaitHandle::EventWaitHandle(SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess )
            : Base( CreateEvent(eventAttributes, name, initiallySet, manualReset, desiredAccess),true)
        {
        }
        HWIN_EXPORT EventWaitHandle::EventWaitHandle(wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess )
            : Base( CreateEvent(nullptr, name, initiallySet, manualReset, desiredAccess),true)
        {
        }
        HWIN_EXPORT EventWaitHandle::EventWaitHandle(bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess )
            : Base( CreateEvent(nullptr, nullptr, initiallySet, manualReset, desiredAccess),true)
        {
        }

        HWIN_EXPORT std::shared_ptr<EventWaitHandle> EventWaitHandle::OpenExisting(wchar_t* name, EventWaitHandleRights desiredAccess, bool inheritHandle)
        {
            auto handle = Open(name,desiredAccess,inheritHandle);
            auto result = std::make_shared<EventWaitHandle>(handle,true);
            return result;
        }


        HWIN_EXPORT bool EventWaitHandle::Set()
        {
            if(!IsValid())
            {
                ThrowInvalidHandle();
            }
            return SetEvent(Value()) != FALSE;
        }
        HWIN_EXPORT bool EventWaitHandle::Reset()
        {
            if(!IsValid())
            {
                ThrowInvalidHandle();
            }
            return ResetEvent(Value()) != FALSE;
        }

        // ----------------------------------------------------------------------
        // AutoResetEvent
        // ----------------------------------------------------------------------
        HWIN_EXPORT AutoResetEvent::AutoResetEvent(bool initiallySet)
            : Base(initiallySet,false)
        {

        }

        // ----------------------------------------------------------------------
        // ManualResetEvent
        // ----------------------------------------------------------------------
        HWIN_EXPORT ManualResetEvent::ManualResetEvent(bool initiallySet)
            : Base(initiallySet,true)
        {

        }

        // ----------------------------------------------------------------------
        // Mutex
        // ----------------------------------------------------------------------
        HWIN_EXPORT HANDLE Mutex::Create( SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallyOwned, MutexRights desiredAccess )
        {
            HANDLE result = CreateMutexExW(eventAttributes, name, initiallyOwned?CREATE_MUTEX_INITIAL_OWNER:0 , DWORD(desiredAccess));
            if(result == nullptr)
            {
                ThrowLastOSError();
            }
            return result;
        }

        HWIN_EXPORT HANDLE Mutex::Open(wchar_t* name, MutexRights desiredAccess, bool inheritHandle )
        {
            HANDLE result = OpenMutexW(DWORD(desiredAccess),inheritHandle,name);
            if(result == nullptr)
            {
                ThrowLastOSError();
            }
            return result;
        }


        HWIN_EXPORT Mutex::Mutex( SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallyOwned, MutexRights desiredAccess )
            : Base(Create(eventAttributes, name, initiallyOwned, desiredAccess),true)
        {
        }
        HWIN_EXPORT Mutex::Mutex( wchar_t* name, bool initiallyOwned , MutexRights desiredAccess )
            : Base(Create(nullptr, name, initiallyOwned, desiredAccess),true)
        {
        }
        HWIN_EXPORT Mutex::Mutex( bool initiallyOwned , MutexRights desiredAccess )
            : Base(Create(nullptr, nullptr, initiallyOwned, desiredAccess),true)
        {
        }
        HWIN_EXPORT std::shared_ptr<Mutex> Mutex::OpenExisting(wchar_t* name, MutexRights desiredAccess , bool inheritHandle)
        {
            HANDLE h = Open(name, desiredAccess , inheritHandle);
            auto result = std::make_shared<Mutex>(h);
            return result;
        }

        HWIN_EXPORT Mutex& Mutex::Release()
        {
            if(ReleaseMutex(GetHANDLE()) == FALSE)
            {
                ThrowLastOSError();
            }
            return *this;
        }

        // ----------------------------------------------------------------------
        // CriticalSection
        // ----------------------------------------------------------------------
        HWIN_EXPORT CriticalSection::CriticalSection()
        {
            InitializeCriticalSection(&criticalSection);
        }

        HWIN_EXPORT CriticalSection::CriticalSection(DWORD spinCount)
        {
            InitializeCriticalSectionAndSpinCount(&criticalSection,spinCount);
        }

        HWIN_EXPORT bool CriticalSection::Wait()
        {
            EnterCriticalSection(&criticalSection);
            return true;
        }

        HWIN_EXPORT bool CriticalSection::TryWait()
        {
            return TryEnterCriticalSection(&criticalSection)?true:false;
        }

        HWIN_EXPORT CriticalSection& CriticalSection::Release()
        {
            LeaveCriticalSection(&criticalSection);
            return *this;
        }




    }
}
